// Copyright 2012 Google Inc. All Rights Reserved.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

'use strict';

var publishers = {};
var advertisers = {};
var domainMap = {};
var numAdvCalls = 0;
var numPubs = 0;

var ganDebug = true;  // set to see debug messages in console

// Shared brain for use by popup.js
var brain = {
  publishers: publishers,
  advertisers: advertisers,
  domainMap: domainMap,
  getDomain: getDomain,
  generatePSL: generatePSL,
  deleteChildren: deleteChildren,
  buildData: buildData,
  ganDebug: ganDebug,
  logDebug: logDebug
};

// returns the first blob after http://, with portnum stripped
function getDomain(url) {
  var parts = url.split('/');
  if (! parts[2]) {
    logDebug('Warning: failed to parse advertiser url: ' + url);
    return;
  }
  return (parts[2].split(':'))[0];
}

function logDebug(msg) {
  if (ganDebug) {
    console.log(msg);
  }
}


// In-memory entity construction ---------------------------------

// This is an ugly chain of callbacks, due to the async nature of API calls.

// The chain begins here, when OAuth is complete.
function buildData(doneCallback) {
  numAdvCalls = 0;
  numPubs = 0;
  domainMap = {};
  publishers = {};
  advertisers = {};
  brain.domainMap = domainMap;
  brain.publishers = publishers;
  brain.advertisers = advertisers;
  getMyPubs(makePubIndex, doneCallback);  // API call
}

function makePubIndex(rawPubs, doneCallback) {
  logDebug('In makePubIndex, publishers found: ');
  logDebug(rawPubs);
  numPubs = rawPubs.length;

  // publishers maps pubID => publisher
  for (var i = 0; i < numPubs; i++) {
    publishers[String(rawPubs[i].id)] = rawPubs[i];
  }

  for (var id in publishers) {
    getMyAdvertisers(id, makeAdvIndex, null, doneCallback);  // recursive
  }
}

function makeAdvIndex(pubID, response, doneCallback) {
  logDebug('in makeAdvIndex, for pubID ' + pubID);
  if (! response) {
    return;
  }
  if (! response.items) {
    logDebug('NOTICE: no advs returned in this request from pubID ' + pubID);
  } else {
    var rawAdvs = response.items;
    logDebug(rawAdvs);
    for (var i in rawAdvs) {
      // advertisers maps advID => advertiser
      advertisers[String(rawAdvs[i].id)] = rawAdvs[i];

      // domainMap maps domain => relationships
      // where relationships is: [{'pubID':id, 'advID':id}... ]
      var domain = getDomain(rawAdvs[i].siteUrl);
      if (! domain) {
        continue;
      }
      var relationships = domainMap[domain];
      if (! relationships) {
        relationships = [];
        domainMap[domain] = relationships;
      }
      relationships.push({'pubID': pubID, 'advID': rawAdvs[i].id});

      // notice alternate domains controlled by advertiser, too
      var otherDomains = rawAdvs[i].redirectDomains;
      if (! otherDomains) {
        continue;
      }
      for (var j in otherDomains) {
        var otherDomain = otherDomains[j];
        var moreRels = domainMap[otherDomain];
        if (! moreRels) {
          moreRels = [];
          domainMap[otherDomain] = moreRels;
        }
        moreRels.push({'pubID': pubID, 'advID': rawAdvs[i].id});
      }
    }
  }
  if (response.nextPageToken) {
    getMyAdvertisers(pubID, makeAdvIndex, response.nextPageToken, doneCallback);
  }

  numAdvCalls++;
  if (numAdvCalls == numPubs) {
    brain.domainMap = domainMap;
    brain.publishers = publishers;
    brain.advertisers = advertisers;
    if (doneCallback) {
      var numAdvs = Object.keys(brain.advertisers).length;
      alert('Finished fetching relationships: ' + numAdvs + ' advertisers found.');
      doneCallback();
    }
  }
}

// utility to clear a tree of nodes
function deleteChildren(obj) {
  if (obj.hasChildNodes()) {
    while (obj.childNodes.length >= 1) {
      obj.removeChild(obj.firstChild);
    }
  }
}



// OAuth helpers -------------------------------------


// Called when GAPI is ready: load GAN API and authorize
function gapiOnLoadCallback() {
  gapi.client.load('gan', 'v1beta1', 
                   function() {
                     logDebug('loading GAN API;  doing oauth...');
                     chrome.experimental.identity.getAuthToken(receiveToken);
                   });
}

function receiveToken(token) {
  if (token) {
    logDebug('oauth complete -- token is:' + token);
    gapi.auth.setToken('token', {'access_token': token}, false);
    buildData(null);
  } else {
    logDebug('oauth failed, did not get token.');
    chrome.tabs.create({'url': './help/notLoggedIn.html'});
  }
}


// GAN API calls  -------------------------------

// Asynchronously fetch publishers controlled by the GAIA account.
// Consumes a callback which consumes the the list of pubs.
function getMyPubs(callback, doneCallback) {
  logDebug('Requesting publishers...');
  var request = gapi.client.gan.publishers.list(
      { 'role': 'publishers',
        'roleId' : '0' });
  request.execute(function(resp) {
      if (resp.items && resp.items.length) {
        callback(resp.items, doneCallback);
      } else {
          chrome.tabs.create({'url': './help/wrongAccount.html'});
      }
    });
}

// Return the raw list of advertisers who have a relationship with pubID
function getMyAdvertisers(pubID, callback, nextToken, doneCallback) {
  logDebug('Requesting advertisers for pubID ' + pubID);
  var params = { 'role': 'publishers',
                 'roleId': pubID,
                 'maxResults': '100',
                 'relationshipStatus': 'approved' };
  if (nextToken) {
    params.pageToken = nextToken;
  }
  var request = gapi.client.gan.advertisers.list(params);
  request.execute(function(resp) {
      logDebug('GOT ADV RESP:');
      logDebug(resp);
      if (resp.items && resp.items.length) {
        callback(pubID, resp, doneCallback);
      } else {
        callback(pubID, null, doneCallback);
      }
    });
}

// Create a new Publisher Specific Link (PSL)
function generatePSL(pubID, advID, PSLname, productURL, callback) {
  logDebug('Calling PSL creation API: ' + pubID + ', ' + advID + ',' +
              PSLname + ', ' + productURL);
  var body = { 'kind': 'gan#link',
               'advertiserId': advID,
               'name': PSLname,
               'destinationUrl': productURL };
  var request = gapi.client.request(
      { 'method': 'POST',
        'path': '/gan/v1beta1/publishers/' + pubID + '/link',
        'body': JSON.stringify(body) });
  request.execute(function(resp) {
      if (resp) {
        logDebug(resp);
        callback(resp.clickTrackingUrl);
      } else {
        alert('API Error: failed to create new link.');
      }
    });
}
